from forest.resource import Resources, Resource, ValidationError

from forest.registry import get_tool
import shlex

import uuid

class MockBlockIds(object):
    def all(self):
        self.devids = devids = {}
        devids['uuid./dev/sda'] = '1234'#str(uuid.uuid4())
        devids['uuid./dev/sda1'] = '1234'#str(uuid.uuid4())
        devids['uuid./dev/sda2'] = '1234'#str(uuid.uuid4())
        devids['uuid./dev/sda3'] = '1234'#str(uuid.uuid4())
        devids['uuid./dev/sda4'] = '1234'#str(uuid.uuid4())

        devids['uuid./dev/sdb'] = '1234'#str(uuid.uuid4())
        devids['uuid./dev/sdb1'] = '1234'#str(uuid.uuid4())
        devids['uuid./dev/sdb2'] = '1234'#str(uuid.uuid4())
        devids['uuid./dev/sdb3'] = '1234'#str(uuid.uuid4())
        devids['uuid./dev/sdb4'] = '1234'#str(uuid.uuid4())
        return devids

class BlockIds(object):
    '''

    If the specified token was found, or if any tags were shown from (specified)
    devices, 0 is returned. If the specified token was not found, or no
    (specified) devices could be identified, an exit code of 2 is returned. For
    usage or other errors, an exit code of 4 is returned.


    '''
    def all(self):
        ''' Parses blkid to get a dictionary of device ids.
        '''
        devids = {}

        for isstderr, line in get_tool('system.poll')(['blkid']):
            devname, flags = line.split(': ')

            flags = flags.strip()
            flagsparsed = {}
            for token in shlex.split(flags):
                flagname, flagval = token.split('=')
                flagsparsed[flagname]=flagval

            if 'UUID' in flagsparsed:
                devids['uuid.'+devname] = flagsparsed['UUID']

        return devids

from cStringIO import StringIO
import fnmatch
import os
import logging
from tokenize import generate_tokens
logger = logging.getLogger('forest.resources.mount')

from forest.resources.common import translate_key, translate_disk_name

class Fstab(object):
    def __init__(self, config, blockids=BlockIds):

        self.blockids = blockids()

        self.config = config
        #self.resources = sorted(list(self.config.get_resources().iter_by_type('mount')), key=lambda x: x.value['mount'])
        self.resources = list(self.config.get_resources().iter_by_type('mount'))
        self.lines = []

        devids = self.blockids.all()

        #print "DEVIDS", devids
        for line in self.resources:
            line = line.value

            if 'uuid' in line:
                dn = translate_disk_name(config, line['uuid'])
                print "READING uuid", line['uuid'], dn

            if not dn.startswith('/dev'):
                dn = os.path.join('/dev', str(dn))

                pieces = ['UUID=%(uuid.' + str(dn) + ')s']
            elif 'device' in line:
                pieces = [line['device']]
            else:
                pieces = ["none"]

            pieces.append(line.get('mount'))
            pieces.append(line.get('fs'))
            pieces.append(line.get('options', 'defaults'))
            pieces.append(str(line.get('dump', '0')))
            pieces.append(str(line.get('order', "0")))
            self.add_line(" ".join(pieces))
            continue

    def add_line(self, line):
        self.lines.append(line)

    def get_lines(self, template_vars = None):
        ''' Returns a set of lines through python template string replacement.

            Args:

                template_vars: a dictionary of template vars to use for each line.

        '''

        template_vars = self.blockids.all()

        logger.info('templates are : {0}, for lines {1}'.format(template_vars, self.lines))

        return [line % template_vars for line in self.lines]


class Mount(Resource):
    ''' All mount resources are collected to create an fstab file for the target.

        One of uuid **OR** device must be specified for each mount.

        Example::

            resources:
              - $: mount
                uuid: disk1[2]
                mount: /boot
                fs: ext4
                dump: 1
                order: 2

              - $: mount
                uuid: disk1[3]
                mount: swap
                fs: swap

              - $: mount
                uuid: disk1[4]
                mount: /
                fs: ext4
                dump: 1
                order: 1

        .. describe:: uuid

            One of uuid or device required. A valid disk name and/or partition.

        .. describe:: device

            One of uuid or device required. A device name.

        .. describe:: fs

            Required. Filesystem type.

        .. describe:: options

            Optional. Filesystem options.

        .. describe:: dump

            Optional. Dump as defined in fstab. used to run fsck.

        .. describe:: order

            Optional. Fsck order as defined in fstab. order to run fsck.

    '''
    __register__ = "mount"

    #not needed yet if that's all it does for now
    #def __init__(self, config, value):
    #    super(Mount, self).__init__(config, value)

    def get_id(self):
        return 'mount:' + self.value.get('mount')

    def get_mount(self):
        return self.value.get('mount')

    def get_fs(self):
        return self.value.get('fs')

    def get_options(self):
        return self.value.get('options', None)

    def get_dump(self):
        return self.value.get('dump', 0)

    def get_order(self):
        return self.value.get('order', 0)

    def get_uuid(self):
        return self.value.get('uuid', None)

    def get_device(self):
        return self.value.get('device', None)

    def validate(self):

        valid_keys = ['mount', 'fs', 'options', 'dump', 'order', 'uuid', 'device']

        for k,v in self.value.iteritems():
            if k not in valid_keys:
                raise ValidationError("Undefined mount option %s" % k)

        if 'mount' not in self.value:
            raise ValidationError("Undefined mount point. Please set mount.")

        if 'fs' not in self.value:
            raise ValidationError("Undefined filesystem. Please set fs.")


        #if 'uuid' not in self.value and 'device' not in self.value :
        #    raise ValidationError("Undefined device. Please set uuid or device.")
